package com.tang.provider;

import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.tang.constant.Cons;
import com.tang.model.Order;
import com.tang.service.StoreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * Created by tangyifei on 2018/7/6.
 */
@Component
public class RabbitMqConsumer {

    private static final Logger logger = LogManager.getLogger(RabbitMqConsumer.class);

    @Resource
    private StoreService storeService;

    /**
     * queues 指定从哪个队列（queue）订阅消息
     * 第一个参数 deliveryTag：就是接受的消息的deliveryTag,可以通过msg.getMessageProperties().getDeliveryTag()获得
     * 第二个参数 multiple：如果为true，确认之前接受到的消息；如果为false，只确认当前消息。
     * 如果为true就表示连续取得多条消息才会发确认，和计算机网络的中tcp协议接受分组的累积确认十分相似，
     * 能够提高效率。
     * 同样的，如果要nack或者拒绝消息（reject）的时候，
     * 也是调用channel里面的basicXXX方法就可以了（要指定tagId）。
     * 注意：如果抛异常或nack（并且requeue为true），消息会重新入队列，
     * 并且会造成消费者不断从队列中读取同一条消息的假象。
     *
     * @param message
     * @param channel
     */
    @RabbitListener(queues = {Cons.TOPIC_QUEUE_NAME_BASIC})
    public void handleBasicMessage(Message message, Channel channel) throws IOException {
        try {
            // 处理消息
            logger.info("消费者处理消息成功，消息是：>>>>>>>>" + new String(message.getBody(), "UTF-8"));
            // System.out.println("OrderConsumer {} handleMessage :"+message);

            // 执行减库存操作
//            storeService.update(new Gson().fromJson(new String(message.getBody()), Order.class));

            // 确认消息
            // 如果 channel.basicAck   channel.basicNack  channel.basicReject 这三个方法都不执行，消息也会被确认 【这个其实并没有在官方看到，不过自己测试的确是这样哈】
            // 所以，正常情况下一般不需要执行 channel.basicAck
            // channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        } catch (Exception e) {
            logger.error("消费者处理消息失败，消息是：>>>>>>>>>>", new String(message.getBody(), "UTF-8"), e);
            // 处理消息失败，将消息重新放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }

    @RabbitListener(queues = {Cons.TOPIC_QUEUE_NAME_OBJECT})
    public void handleObjectMessage(Message message, Channel channel) throws IOException {
        try {
            // 处理消息
            logger.info("消费者处理消息成功，消息是：>>>>>>>>" + new String(message.getBody()));
            // System.out.println("OrderConsumer {} handleMessage :"+message);

            // 执行减库存操作
            if(null != message){
                storeService.update(new Gson().fromJson(new String(message.getBody()), Order.class));
            }

            // 确认消息
            // 如果 channel.basicAck   channel.basicNack  channel.basicReject 这三个方法都不执行，消息也会被确认 【这个其实并没有在官方看到，不过自己测试的确是这样哈】
            // 所以，正常情况下一般不需要执行 channel.basicAck
            // channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        } catch (Exception e) {
            logger.error("消费者处理消息失败，消息是：>>>>>>>>>>", new String(message.getBody()), e);
            // 处理消息失败，将消息重新放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }

}
